package com.hk.core.autoconfigure.data.database;

import com.hk.commons.multischema.DataSourceType;
import com.hk.core.jdbc.tenants.CompositeTenantIdResolver;
import com.hk.core.jdbc.tenants.UserTenantAware;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;

import java.util.Objects;

/**
 * 多数据库切换切面，
 * 注意: 要将 spring.jpa.open-in-view 设置为 false
 * <a href="https://www.jianshu.com/p/c856799a42a4" />
 *
 * @author Kevin
 * @date 2023-10-25 22:24
 * @see JpaBaseConfiguration.JpaWebConfiguration#openEntityManagerInViewInterceptor()
 */
@Aspect
@Order(value = Integer.MIN_VALUE)
record DataSourceAspect(UserTenantAware userTenantAware, CompositeTenantIdResolver resolver) {

    /**
     * annotation  标注的默认不能写在接口上，只能写在实现类上
     */
//    @Pointcut("@annotation(com.hk.core.datasource.DataSourceType)")
    @Pointcut(value = "execution(* com.hk..repository.*.*(..))")
    public void pointcut() {

    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        var target = point.getTarget();
        String dataSourceName = null;
        DataSourceType sourceType;
        if (point.getSignature() instanceof MethodSignature signature &&
                Objects.nonNull(sourceType = signature.getMethod().getAnnotation(DataSourceType.class))) {
            dataSourceName = sourceType.value();
        } else {
            sourceType = AnnotationUtils.findAnnotation(target.getClass(), DataSourceType.class);
            if (Objects.nonNull(sourceType)) {
                dataSourceName = sourceType.value();
            }
        }
        userTenantAware.setCurrentTenantId(resolver.resolve(dataSourceName));
        try {
            return point.proceed();
        } finally {
            userTenantAware.clearCurrentTenantId();
        }
    }
}
